Skip to content

METAL-1730: Add stream selection for multi-version support#175

Open
honza wants to merge 1 commit intoopenshift:mainfrom
honza:dual-stream
Open

METAL-1730: Add stream selection for multi-version support#175
honza wants to merge 1 commit intoopenshift:mainfrom
honza:dual-stream

Conversation

@honza
Copy link
Copy Markdown
Member

@honza honza commented Apr 14, 2026

Enable the image-customization-controller to serve different CoreOS base images (e.g., RHCOS 9 vs RHCOS 10) based on a stream label on PreprovisioningImage resources. Images are now indexed by (stream, architecture) and discovered from stream-prefixed filenames like ironic-python-agent-rhel-9.iso. The coreos.openshift.io/stream label is read from PPI metadata and falls back to the default stream when absent.

How it works

The coreos.openshift.io/stream label on a BareMetalHost determines
which kernel, initrd, and rootfs files are used during PXE boot. For
example, a BMH labeled coreos.openshift.io/stream: rhel-10 will PXE
boot with ironic-python-agent-rhel-10.kernel,
ironic-python-agent-rhel-10.initramfs, and
ironic-python-agent-rhel-10.rootfs instead of the unqualified defaults.

The per-node coreos.live.rootfs_url kernel parameter set via ICC's
ExtraKernelParams overrides the global one from ironic-image because
dracut's getarg returns the last value for duplicate parameters.

Changes across repos

installer (openshift/installer#10502)
pkg/asset/machines/baremetal/: Sets the coreos.openshift.io/stream
label on all generated BareMetalHost objects (control-plane, worker,
arbiter) based on the install-config's OSImageStream setting, defaulting
to rhel-9. Configures hostSelector.matchLabels on
BareMetalMachineProviderSpec so MachineSets only claim BMHs with the
matching stream label.

cluster-baremetal-operator (openshift/cluster-baremetal-operator#590)
provisioning/image_customization.go: Passes three new environment
variables to the ICC container: DEPLOY_KERNEL (path to the kernel file),
IMAGE_SHARED_DIR (path to the shared images directory where
stream-prefixed files are discovered), and IRONIC_ROOTFS_URL (base URL
for the rootfs served by httpd). These enable ICC to discover multi-stream
images and construct per-node kernel/rootfs URLs.

machine-os-images (openshift/machine-os-images#83)
scripts/copy-metal: Creates stream-prefixed IPA symlinks (e.g.
ironic-python-agent-rhel-9.iso, ironic-python-agent-rhel-10.kernel)
for all available CoreOS versions, allowing ICC to discover images by
stream. Also fixes missing initramfs symlinks in the fixed=true (PXE)
block — without these, a stream-specific kernel would be paired with the
default-stream initrd, causing a version mismatch that crashes immediately
on PXE boot.

image-customization-controller (openshift/image-customization-controller#175)
pkg/imagehandler/, pkg/imageprovider/rhcos.go: Adds OS stream
selection so ICC can serve different CoreOS base images based on the
coreos.openshift.io/stream label. Images are indexed by (stream,
architecture) and discovered from stream-prefixed filenames. Key changes:

  • streamArchSpecificURL() transforms the base rootfs URL into a stream-
    and arch-specific URL
  • BuildImage() passes the stream to ServeKernel() and uses the
    stream-specific rootfs URL in ExtraKernelParams
  • imageKey() includes the stream in the cache key so that changing a
    BMH's stream label invalidates the cached image

Summary by CodeRabbit

  • New Features

    • Added support for stream-aware OS image selection and serving. The system now recognizes and manages multiple OS image streams (e.g., different release variants) and serves appropriate images and kernels based on the selected stream.
  • Bug Fixes

    • Improved base image resolution to handle stream-specific configurations correctly, with fallback logic for default streams.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 14, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository: openshift/coderabbit/.coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 16580f71-0806-452e-94af-ce76a33eb18a

📥 Commits

Reviewing files that changed from the base of the PR and between 7319a87 and b6cba19.

📒 Files selected for processing (7)
  • cmd/static-server/main.go
  • cmd/static-server/main_test.go
  • pkg/imagehandler/imagefile.go
  • pkg/imagehandler/imagefilesystem.go
  • pkg/imagehandler/imagehandler.go
  • pkg/imagehandler/imagehandler_test.go
  • pkg/imageprovider/rhcos.go
✅ Files skipped from review due to trivial changes (1)
  • pkg/imagehandler/imagefile.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • pkg/imagehandler/imagehandler.go

Walkthrough

Adds a stream dimension to image selection and serving: new stream fields/keys, stream-aware filename parsing and lookup, updated ImageHandler API to accept stream, and propagation of stream through provider and static-server call sites and tests. Fallbacks to default stream and host-architecture are preserved.

Changes

Stream-aware image selection and serving

Layer / File(s) Summary
Data Shape
pkg/imagehandler/imagefile.go
Add stream string field to imageFile.
Index / Keying
pkg/imagehandler/imagehandler.go
Introduce streamArchKey and update internal iso/initramfs/kernel maps to use (stream, arch) keys. Extend osImage to include stream.
Filename Parsing / Loading
pkg/imagehandler/imagehandler.go
Add matchStreamFilename and update loadOSImage to parse and populate stream and prefer stream+arch matches when loading images.
Core Lookup Logic
pkg/imagehandler/imagehandler.go
Implement getBaseImage(arch, stream, initramfs) and getKernel(arch, stream) with exact (stream, arch) lookup, fallback to default stream for arch, then host-arch fallbacks. Return explicit InvalidBaseImageError when no base exists for (arch, stream, initramfs).
Public API / Caching
pkg/imagehandler/imagehandler.go
Add stream parameter to ServeImage and ServeKernel signatures, include stream in cache keys, and store stream in created imageFile cache entries. Update HasImagesForArchitecture to consider all streams.
Wiring / Consumers
pkg/imageprovider/rhcos.go, cmd/static-server/main.go, pkg/imagehandler/imagefilesystem.go
Extract stream from image metadata (streamFromImageData) and include it in imageKey; pass stream into ImageHandler.ServeImage and ServeKernel. Static server call adjusted to provide stream argument; imagefilesystem.Open now forwards im.stream to getKernel/getBaseImage.
URL helper behavior
pkg/imageprovider/rhcos.go
Replace archSpecificURL with streamArchSpecificURL, optionally appending -<stream> and conditionally appending _<arch> for non-host architectures while preserving query/fragment.
Tests
pkg/imagehandler/imagehandler_test.go, cmd/static-server/main_test.go
Update test doubles and fixtures to accept/pass stream; refactor test fixtures to use streamArchKey keys; add TestImagePatternWithStream and TestHasImagesForArchitectureWithStreams; update kernel/ISO/initramfs expectations and call sites to include stream.

Sequence Diagram

sequenceDiagram
    participant Client
    participant ImageHandler
    participant ImageSelection
    participant ImageRetrieval

    Client->>ImageHandler: ServeImage(key, arch, stream, ...)
    ImageHandler->>ImageSelection: Lookup image by (stream, arch, initramfs)
    
    alt Exact (stream,arch) found
        ImageSelection->>ImageRetrieval: Return image file for (stream,arch)
        ImageRetrieval-->>ImageSelection: Image data/path
    else Fallback to default stream for arch
        ImageSelection->>ImageSelection: Retry (default_stream, arch)
        ImageSelection->>ImageRetrieval: Return image file
        ImageRetrieval-->>ImageSelection: Image data/path
    else Fallback to host architecture
        ImageSelection->>ImageSelection: Retry (stream/default, host_arch)
        ImageSelection->>ImageRetrieval: Return image file
        ImageRetrieval-->>ImageSelection: Image data/path
    else No base image
        ImageSelection-->>ImageHandler: nil / InvalidBaseImageError
    end

    ImageSelection-->>ImageHandler: imageFile (with stream metadata)
    ImageHandler-->>Client: served image URL/path
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

🚥 Pre-merge checks | ✅ 11 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 15.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (11 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The pull request title clearly and concisely summarizes the main change: adding stream selection capability for multi-version CoreOS image support. The title directly maps to the primary objective described in the PR objectives.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Stable And Deterministic Test Names ✅ Passed All test names in the PR are stable and deterministic. The repository uses standard Go testing, not Ginkgo. New tests use only static, descriptive test case names with no dynamic values.
Test Structure And Quality ✅ Passed Check not applicable - this codebase uses standard Go testing, not Ginkgo. New tests follow established patterns with proper cleanup, focused responsibility, and contextual assertion messages.
Microshift Test Compatibility ✅ Passed This PR adds only standard Go unit tests (not Ginkgo e2e tests), so the MicroShift compatibility check is not applicable.
Single Node Openshift (Sno) Test Compatibility ✅ Passed No Ginkgo e2e tests are added in this PR. The repository is the image-customization-controller, which uses standard Go unit tests only. Custom check is not applicable.
Topology-Aware Scheduling Compatibility ✅ Passed This PR modifies only image handling utility code. No deployment manifests, scheduling constraints, pod affinity rules, or topology-aware pod configurations are introduced.
Ote Binary Stdout Contract ✅ Passed The PR contains no violations of the OTE Binary Stdout Contract. All logging uses zap which defaults to stderr, and no direct stdout writes were introduced in process-level code.
Ipv6 And Disconnected Network Test Compatibility ✅ Passed No Ginkgo e2e tests added. Only standard Go unit tests (TestImagePatternWithStream, TestHasImagesForArchitectureWithStreams) using *testing.T. Custom check not applicable.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands and usage tips.

@openshift-ci openshift-ci Bot requested review from dtantsur and zaneb April 14, 2026 17:29
@openshift-ci
Copy link
Copy Markdown
Contributor

openshift-ci Bot commented Apr 14, 2026

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: honza

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@openshift-ci openshift-ci Bot added the approved Indicates a PR has been approved by an approver from all required OWNERS files. label Apr 14, 2026
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@pkg/imagehandler/imagehandler.go`:
- Around line 417-419: HasImagesForArchitecture currently probes only the
empty/default stream and misses stream-qualified assets; update it to be
stream-aware by iterating over the set of known streams on imageFileSystem (e.g.
the method/property that lists available streams such as
availableStreams()/streams) and for each stream call getBaseImage(arch, stream,
false) and getBaseImage(arch, stream, true) (same pair of checks ServeImage
would use) and return true if any call returns non-nil; if the struct has no
stream-listing API, fall back to checking the empty stream plus any stream
values ServeImage accepts so the detection matches ServeImage behavior (refer to
HasImagesForArchitecture, getBaseImage and ServeImage).

In `@pkg/imageprovider/rhcos.go`:
- Around line 157-170: The function streamArchSpecificURL treats an empty arch
as a distinct architecture and produces a bogus “_” suffix; update
streamArchSpecificURL to normalize empty architecture to mean host by setting
arch to env.HostArchitecture() when arch == "" (or otherwise capture hostArch :=
env.HostArchitecture() and treat empty arch as hostArch) and then only append
the "_<arch>" suffix when arch != hostArch; reference streamArchSpecificURL and
env.HostArchitecture() when making this change.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository: openshift/coderabbit/.coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: ec6b8ae2-9fdb-4350-a82b-541bb604193d

📥 Commits

Reviewing files that changed from the base of the PR and between a43d9c9 and 7319a87.

📒 Files selected for processing (7)
  • cmd/static-server/main.go
  • cmd/static-server/main_test.go
  • pkg/imagehandler/imagefile.go
  • pkg/imagehandler/imagefilesystem.go
  • pkg/imagehandler/imagehandler.go
  • pkg/imagehandler/imagehandler_test.go
  • pkg/imageprovider/rhcos.go

Comment thread pkg/imagehandler/imagehandler.go
Comment thread pkg/imageprovider/rhcos.go
@honza honza changed the title Add stream selection for multi-version support METAL-1730: Add stream selection for multi-version support Apr 14, 2026
@openshift-ci-robot openshift-ci-robot added the jira/valid-reference Indicates that this PR references a valid Jira ticket of any type. label Apr 14, 2026
@openshift-ci-robot
Copy link
Copy Markdown

openshift-ci-robot commented Apr 14, 2026

@honza: This pull request references METAL-1730 which is a valid jira issue.

Warning: The referenced jira issue has an invalid target version for the target branch this PR targets: expected the story to target the "5.0.0" version, but no target version was set.

Details

In response to this:

Enable the image-customization-controller to serve different CoreOS base images (e.g., RHCOS 9 vs RHCOS 10) based on a stream label on PreprovisioningImage resources. Images are now indexed by (stream, architecture) and discovered from stream-prefixed filenames like ironic-python-agent-rhel-9.iso. The coreos.openshift.io/stream label is read from PPI metadata and falls back to the default stream when absent.

How it works

The coreos.openshift.io/stream label on a BareMetalHost determines
which kernel, initrd, and rootfs files are used during PXE boot. For
example, a BMH labeled coreos.openshift.io/stream: rhel-10 will PXE
boot with ironic-python-agent-rhel-10.kernel,
ironic-python-agent-rhel-10.initramfs, and
ironic-python-agent-rhel-10.rootfs instead of the unqualified defaults.

The per-node coreos.live.rootfs_url kernel parameter set via ICC's
ExtraKernelParams overrides the global one from ironic-image because
dracut's getarg returns the last value for duplicate parameters.

Changes across repos

installer (openshift/installer#10502)
pkg/asset/machines/baremetal/: Sets the coreos.openshift.io/stream
label on all generated BareMetalHost objects (control-plane, worker,
arbiter) based on the install-config's OSImageStream setting, defaulting
to rhel-9. Configures hostSelector.matchLabels on
BareMetalMachineProviderSpec so MachineSets only claim BMHs with the
matching stream label.

cluster-baremetal-operator (openshift/cluster-baremetal-operator#590)
provisioning/image_customization.go: Passes three new environment
variables to the ICC container: DEPLOY_KERNEL (path to the kernel file),
IMAGE_SHARED_DIR (path to the shared images directory where
stream-prefixed files are discovered), and IRONIC_ROOTFS_URL (base URL
for the rootfs served by httpd). These enable ICC to discover multi-stream
images and construct per-node kernel/rootfs URLs.

machine-os-images (openshift/machine-os-images#83)
scripts/copy-metal: Creates stream-prefixed IPA symlinks (e.g.
ironic-python-agent-rhel-9.iso, ironic-python-agent-rhel-10.kernel)
for all available CoreOS versions, allowing ICC to discover images by
stream. Also fixes missing initramfs symlinks in the fixed=true (PXE)
block — without these, a stream-specific kernel would be paired with the
default-stream initrd, causing a version mismatch that crashes immediately
on PXE boot.

image-customization-controller (openshift/image-customization-controller#175)
pkg/imagehandler/, pkg/imageprovider/rhcos.go: Adds OS stream
selection so ICC can serve different CoreOS base images based on the
coreos.openshift.io/stream label. Images are indexed by (stream,
architecture) and discovered from stream-prefixed filenames. Key changes:

  • streamArchSpecificURL() transforms the base rootfs URL into a stream-
    and arch-specific URL
  • BuildImage() passes the stream to ServeKernel() and uses the
    stream-specific rootfs URL in ExtraKernelParams
  • imageKey() includes the stream in the cache key so that changing a
    BMH's stream label invalidates the cached image

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the openshift-eng/jira-lifecycle-plugin repository.

Enable the image-customization-controller to serve different CoreOS
base images (e.g., RHCOS 9 vs RHCOS 10) based on a stream label
on PreprovisioningImage resources. Images are now indexed by
(stream, architecture) and discovered from stream-prefixed filenames
like ironic-python-agent-rhel-9.iso. The coreos.openshift.io/stream
label is read from PPI metadata and falls back to the default stream
when absent.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@openshift-ci
Copy link
Copy Markdown
Contributor

openshift-ci Bot commented May 7, 2026

@honza: The following tests failed, say /retest to rerun all failed tests or /retest-required to rerun all mandatory failed tests:

Test name Commit Details Required Rerun command
ci/prow/e2e-metal-ipi-serial-ipv4 b6cba19 link true /test e2e-metal-ipi-serial-ipv4
ci/prow/e2e-metal-ipi-bm b6cba19 link true /test e2e-metal-ipi-bm

Full PR test history. Your PR dashboard.

Details

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository. I understand the commands that are listed here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

approved Indicates a PR has been approved by an approver from all required OWNERS files. jira/valid-reference Indicates that this PR references a valid Jira ticket of any type.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants